Skip to content

chore(contexts): Remove default-trace-ID option#5979

Open
thetruecpaul wants to merge 2 commits into
masterfrom
cpaul/051126/ship-default-trace-id
Open

chore(contexts): Remove default-trace-ID option#5979
thetruecpaul wants to merge 2 commits into
masterfrom
cpaul/051126/ship-default-trace-id

Conversation

@thetruecpaul
Copy link
Copy Markdown
Contributor

Shipped this a week ago and have seen no problems. 👍

@thetruecpaul thetruecpaul requested a review from a team May 11, 2026 22:08
@thetruecpaul thetruecpaul requested a review from a team as a code owner May 11, 2026 22:08
@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch from 04f1690 to a55366d Compare May 11, 2026 22:12
Comment thread relay-event-normalization/src/event.rs
Copy link
Copy Markdown
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, for coming back and cleaning this up.

You accidentally updated/reverted the submodules, sentry-conventions and sentry-native. You might be missing a git submodule update --recursive --init.

Comment thread relay-event-normalization/src/event.rs
Comment thread CHANGELOG.md Outdated
@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch 2 times, most recently from 274748d to 4654bdc Compare May 12, 2026 17:50
Comment thread relay-event-normalization/src/event.rs
@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch from 4654bdc to de252d4 Compare May 12, 2026 19:38
Comment thread relay-event-normalization/src/event.rs Outdated
Comment thread relay-event-normalization/src/event.rs Outdated
Comment thread relay-event-normalization/src/event.rs Outdated
.0
.is_none()
{
contexts.add(TraceContext::random(event_id))
Copy link
Copy Markdown
Member

@shashjar shashjar May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's a TraceContext with valid information but it's missing trace_id (not sure if this actually occurs in practice) - I think we'll end up overriding the entire context with a new one that has only trace_id? I think we want to get_or_default the trace context, then check whether trace_context.trace_id is null, and then set only trace_context.trace_id if so

Comment thread relay-event-normalization/src/event.rs Outdated
// If the event lacks a TraceContext, add a random one.

if config.derive_trace_id && !contexts.contains::<TraceContext>() {
if !contexts.contains::<TraceContext>()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add unit tests for this logic somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating the tests is what I'm working on now! :-)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a drive by comment before bed, an integration test (if it doesn't already exist) would be great, we do have a decent framework for them and they work great for regression tests.

@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch from de252d4 to 9942b0b Compare May 12, 2026 23:27
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9942b0b. Configure here.

Comment thread relay-event-normalization/src/event.rs
Comment thread relay-event-normalization/src/event.rs
@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch from 9942b0b to e4b0067 Compare May 18, 2026 20:05
Comment on lines +1370 to 1374
let trace_ctx = contexts.get_or_default::<TraceContext>();
if trace_ctx.trace_id.0.is_none() {
trace_ctx.trace_id = Annotated::new(TraceId::from(event_id))
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: If an event has an existing but empty TraceContext, the normalization logic only sets the trace_id, leaving the required span_id unset, creating an inconsistent state.
Severity: MEDIUM

Suggested Fix

In the else branch where contexts.contains::<TraceContext>() is true, ensure that span_id is also set along with trace_id. This could be done by generating a new span_id if trace_ctx.span_id is None, similar to how trace_id is handled. Alternatively, consider replacing the partial context with a fully new one using TraceContext::random() if the existing one is incomplete.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: relay-event-normalization/src/event.rs#L1370-L1374

Potential issue: When an event is processed that already contains a `TraceContext` but
lacks a `trace_id` (e.g., `{"contexts": {"trace": {}}}`), the normalization logic enters
a branch that sets the `trace_id`. However, this logic fails to also generate and set
the `span_id`. This is inconsistent with the case where no `TraceContext` exists, in
which a new context is created with both a `trace_id` and a `span_id` via
`TraceContext::random()`. The result is a partially normalized trace context where the
`span_id`, a required field, remains `None`.

@thetruecpaul thetruecpaul requested a review from Dav1dde May 18, 2026 20:14
@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch from e4b0067 to f98e9c9 Compare May 18, 2026 20:18
@thetruecpaul
Copy link
Copy Markdown
Contributor Author

Adding the skip-changelog label for the Python changelog since it's just updating a couple tests

@thetruecpaul thetruecpaul force-pushed the cpaul/051126/ship-default-trace-id branch from f98e9c9 to c024935 Compare May 18, 2026 20:27
@thetruecpaul thetruecpaul requested a review from a team May 18, 2026 20:37
assert set(event["contexts"].keys()) == {"browser", "client_os", "trace"}
else:
assert "contexts" not in event
assert set(event["contexts"].keys()) == {"trace"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we assert the fields contained in the trace context, e.g. trace_id or other necessary fields are present?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally a good idea, but these are the Python library tests and this is a test for normalizing the user agent, so I think this is fine.

// If the event lacks a TraceContext, add a random one.

if config.derive_trace_id && !contexts.contains::<TraceContext>() {
if !contexts.contains::<TraceContext>() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could collapse this if/else with something like the below (does span_id default have to be added as well?)

let trace_ctx = contexts.get_or_default::<TraceContext>();
if trace_ctx.trace_id.0.is_none() {
    trace_ctx.trace_id = Annotated::new(TraceId::from(event_id))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, there are four possible cases:

  1. The TraceContext is not set. In this case, we generate a random SpanID and a TraceID from the EventID. (See TraceContext::random.)
  2. The TraceContext is set with a TraceID and SpanID. In this case, we want to do nothing.
  3. The TraceContext is set with no TraceID but an existing SpanID. In this case, we want to create new TraceID but not change the existing SpanID.
  4. The TraceContext is set with no TraceID and no SpanID. In this case, we are currently only generating the TraceID. My reasoning is that "empty context" is different from "missing context" and we should only make the minimal update needed for our purposes (ingesting to EAP), but I could be talked around to updating both for the purpose of consistency with case (1).

All this to say — what does default mean for TraceContext? Is it something with all the attributes set to None? Ideally we'd want to short-circuit to TraceContext::random if the context doesn't exist, not TraceContext::default or whatnot (this method doesn't exist).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say we should defer to @Dav1dde on the question of default span ID (whether or not that should be added, and where that should be added, or if it is already).

But I think the code I attached above works for all of the cases in which trace ID needs to be populated (and additional if branches can be added if needed if there are other defaults we need to set)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I look into this I begin to question whether this doesn't have a lot of unintentional side-effects.
We're generating a trace context here for the purpose of storing in EAP, but that context is also visible to users with side effects as running it through dynamic sampling to mark the sampling decision on the error.
That's more a fundamental concern which also was brought up with the introduction of the feature flag.

I would say we should defer to @Dav1dde on the question of default span ID (whether or not that should be added, and where that should be added, or if it is already).

The span_id is required on the context, that means if you want to generate a context it also needs to be filled with a span_id.

All this to say — what does default mean for TraceContext? Is it something with all the attributes set to None?

All attributes set to Annotated::empty() (practically None). There is an argument to be made, that it should not implement Default at all.

Copy link
Copy Markdown
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through it, I noticed some bigger problems. This is actually right now applied to transactions, which makes an existing validation of a trace context being required order dependent, in the worst case it will invent trace or span ids for transactions.

Some of the metadata stuff is inconsistent, with no context metadata is added, with a partial context it is not (presumably because there is this assumption that the schema processor would add it?).
I think we should just not bother with adding metadata to the individual fields when the context is created. If a metadata is desired, we should instead attach it to the context element which was created.

I really don't like that we have to touch a lot of unrelated tests, so I think we should keep the normalization option and that is also a great place to add some documentation why we're doing this.

Relay's normalization is complex enough that I made changes and notes during the review, ended up polishing it and making it a PR with the changes I describe here: #6009

assert set(event["contexts"].keys()) == {"browser", "client_os", "trace"}
else:
assert "contexts" not in event
assert set(event["contexts"].keys()) == {"trace"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally a good idea, but these are the Python library tests and this is a test for normalizing the user agent, so I think this is fine.

Comment thread relay-ua/uap-core
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another presumably unintentional submodule revert

// If the event lacks a TraceContext, add a random one.

if config.derive_trace_id && !contexts.contains::<TraceContext>() {
if !contexts.contains::<TraceContext>() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I look into this I begin to question whether this doesn't have a lot of unintentional side-effects.
We're generating a trace context here for the purpose of storing in EAP, but that context is also visible to users with side effects as running it through dynamic sampling to mark the sampling decision on the error.
That's more a fundamental concern which also was brought up with the introduction of the feature flag.

I would say we should defer to @Dav1dde on the question of default span ID (whether or not that should be added, and where that should be added, or if it is already).

The span_id is required on the context, that means if you want to generate a context it also needs to be filled with a span_id.

All this to say — what does default mean for TraceContext? Is it something with all the attributes set to None?

All attributes set to Annotated::empty() (practically None). There is an argument to be made, that it should not implement Default at all.

} else {
let trace_ctx = contexts.get_or_default::<TraceContext>();
if trace_ctx.trace_id.0.is_none() {
trace_ctx.trace_id = Annotated::new(TraceId::from(event_id))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this inconsistent with TraceContext::random which does add metadata for a "missing" trace id?

Comment on lines -172 to -173
/// Should add a random trace ID to events that lack one.
pub derive_trace_id: bool,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this here, add some documentation why this is needed and then just permanently set it to true where the config gets created.

Usually I'd agree with just by default enabling it, but there is a large impact on tests which are testing unrelated things but now need to assert a random trace id and context.

We can still test the context creation with the boolean and have integration tests to ensure this actually is enabled.


if config.derive_trace_id && !contexts.contains::<TraceContext>() {
if !contexts.contains::<TraceContext>() {
contexts.add(TraceContext::random(event_id))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TraceContext::random interior should not add metadata to the individual fields that they were missing (trace_id/span_id).

If you want to make the trace context effectively required, instead we should add this marker to the context itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants